package com.tcm.common.handler;

import com.tcm.common.enums.ResponseEnum;
import com.tcm.entity.resp.R;
import com.tcm.exception.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;

/**
 * 全局的系统异常处理器
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    public static final Logger logger= LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 自定义一次处理
     * @param e 自定义异常
     * @return 响应异常信息
     */
    @ExceptionHandler({DatabaseOperationException.class,
            EntityAlreadyExistException.class,
            EntityInvalidException.class,
            UserNotExistException.class,
            UserTypeNotInvalidException.class})
    public R doCustomException(Exception e){
        return R.fail(0, e.getMessage());
    }

    /**
     * 约束异常
     * @param e 异常对象
     * @return 响应
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public R doDuplicateKeyException(DuplicateKeyException e){
        if (e.getMessage().contains("ts_clazz")) {
            return R.fail(0, "班级名已存在");
        }else if (e.getMessage().contains("ts_user")){
            return R.fail(0, "用户账号已存在");
        }else {
            return R.fail(0, e.getMessage());
        }
    }

    /**
     * Servlet参数校验异常
     * @param e 异常对象
     * @return 响应
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public R doMissingServletRequestParameterException(MissingServletRequestParameterException e){
        return R.fail(102, "类型为【" + e.getParameterType() + "】的必要参数【" + e.getParameterName() + "】不能为空");
    }

    /**
     * JSON请求校验结果，也就是请求中对实体标记了@RequestBody
     * @param e 异常对象
     * @return 响应结果
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public R handMethodArgumentNotValidException(MethodArgumentNotValidException e){
        // 拼接响应结果
        BindingResult bindingResult = e.getBindingResult();
        return extractException(bindingResult.getAllErrors());
    }

    /**
     * 表单请求校验结果处理
     * @param bindException 异常请求对象
     * @return 响应结果
     */
    @ExceptionHandler(value = BindException.class)
    public R errorHandler(BindException bindException) {
        BindingResult bindingResult = bindException.getBindingResult();
        return extractException(bindingResult.getAllErrors());
    }

    /**
     * 请求方式不支持异常
     * @param e 异常对象
     * @return 异常响应
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R notSupportMethodException(HttpRequestMethodNotSupportedException e){
        return R.fail(101, e.getMessage());
    }

    /**
     * 打印异常日志，以便复现异常场景
     * @param request
     * @param response
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public R handException(HttpServletRequest request, HttpServletResponse response, Exception e){
        logger.error("\n*************************************************************************************************************************************************************************************");
        String method = request.getMethod();
        logger.error("异常请求："+request.getRequestURL());
        logger.error("异常请求方式："+method);
        if (method.equals("POST")) {
            logger.error("异常请求参数："+getParam(request));
        }else if (method.equals("GET")) {
            logger.error("异常请求参数："+request.getQueryString());
        }
        //打印异常信息
        logger.error("异常追踪栈：");
        logger.error("\n" + e.getMessage());
        logger.error("\n" + getTrace(e));
        logger.error("\n*************************************************************************************************************************************************************************************");
        return R.fail(ResponseEnum.SYSTEM_ERROR.getCode(), ResponseEnum.SYSTEM_ERROR.getMsg());
    }

    /**
     * 获取异常跟踪栈信息
     * @param throwable
     * @return
     */
    public String getTrace(Throwable throwable){
        StringWriter stringWriter=new StringWriter();
        PrintWriter writer=new PrintWriter(stringWriter);
        throwable.printStackTrace(writer);
        StringBuffer buffer=stringWriter.getBuffer();
        return buffer.toString();
    }

    /**
     * 获取异常跟踪栈信息
     * @param throwable
     * @return
     */
    public static String getTraceToString(Throwable throwable){
        StringWriter stringWriter=new StringWriter();
        PrintWriter writer=new PrintWriter(stringWriter);
        throwable.printStackTrace(writer);
        StringBuffer buffer=stringWriter.getBuffer();
        return buffer.toString();
    }

    /**
     * 获取POST请求参数
     * @param request
     * @return
     */
    public String getParam(HttpServletRequest request) {
        ServletInputStream inputStream = null;
        String result="";
        try {
            inputStream = request.getInputStream();
            int length=request.getContentLength();
            byte[] bytes=new byte[length];
            inputStream.read(bytes);
            result=new String(bytes,"utf-8");
        } catch (IOException exception) {
            exception.printStackTrace();
        }
        return result;
    }

    /**
     * 数据校验异常响应结果封装方法
     * @param errorList 错误列表
     * @return 响应结果
     */
    private R extractException(List<ObjectError> errorList) {
        StringBuilder errorMsg = new StringBuilder();
        for (ObjectError objectError : errorList) {
            errorMsg.append(objectError.getDefaultMessage()).append(";");
        }
        // 移出最后的分隔符
        errorMsg.delete(errorMsg.length() - 1, errorMsg.length());
        return R.fail(101, errorMsg.toString());
    }
}
